package com.googlecode.cswish.struts;

import java.util.Map;

import ognl.OgnlException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.googlecode.cswish.model.ModelInfo;
import com.googlecode.cswish.model.PropertyInfo;
import com.googlecode.cswish.struts.spring.BeanIntrospector;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.googlecode.cswish.util.StringUtil;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.ognl.accessor.XWorkListPropertyAccessor;
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;

/**
 * Find list type according to the request path
 * 
 * @author feng Date: 2009-5-23
 */
public class ListPropertyAccessor extends XWorkListPropertyAccessor {
	protected final Log logger = LogFactory.getLog(getClass());
	
	private BeanIntrospector beanIntrospector;

	@Inject
	public void setObjectFactory(
			com.opensymphony.xwork2.ObjectFactory objectFactory) {
		ObjectFactory factory = (ObjectFactory) objectFactory;
		beanIntrospector = factory.getBean(BeanIntrospector.class, false);
		
		super.setObjectFactory(objectFactory);
	}
	
	@Override
	public Object getProperty(Map context, Object target, Object name)
    	throws OgnlException {
		initLastAccessed(context);
		
		return super.getProperty(context, target, name);
	}
	
	@Override
	public void setProperty(Map context, Object target, Object name, Object value)
    	throws OgnlException {
		initLastAccessed(context);
		
		super.setProperty(context, target, name, value);
	}
	
	public void initLastAccessed(Map context)
    	throws OgnlException {
		String lastPath = (String) context.get("cached.list.path");
		String path = ReflectionContextState.getCurrentPropertyPath(context);
		
		Class<?> theParentClass;
		String theProperty;
//		Class<?> theLastPropertyClass;
		if (path.equals(lastPath)) {
			Object[] objs = (Object[]) context.get("cached.list.accessed");
			theParentClass = (Class<?>) objs[0];
			theProperty = (String) objs[1];
//			theLastPropertyClass = (Class<?>) objs[1];
		} else {
			ActionContext actionContext = ActionContext.getContext();
			Object action = actionContext.getActionInvocation().getAction();
			
			// Find the correct class type according the property path
			// path format:  model.1.id  <==> model[1].id
			String[] properties = StringUtil.split(path, '.');
			theParentClass = action.getClass();
			ModelInfo modelInfo = beanIntrospector.getModelInfo(theParentClass.getName());
			theProperty = properties[0];
			PropertyInfo info = modelInfo.getProperty(properties[0]);
			
			if (info != null) {
				Class<?> theLastPropertyClass = info.getGenericTypeClass();
				
				if (modelInfo != null && properties.length > 1) {
					for (int i = 1; i < properties.length; i++) {
						// find the parent class
						modelInfo = beanIntrospector.getModelInfo(theLastPropertyClass.getName());
						
						PropertyInfo propertyInfo = modelInfo.getProperty(properties[i]);
						if (propertyInfo != null) {		// it's valid property name
							theProperty = properties[i];
							theParentClass = theLastPropertyClass;
							theLastPropertyClass = propertyInfo.getGenericTypeClass();
						} else {
							// skip '.'
						}
					}
				}
			} else {
				logger.error("Can't find the property: " + properties[0] + " from model:" + theParentClass.getName());
			}
			
//			theLastPropertyClass = getGenericTypeClass(theParentClass.getName(), modelInfo.getProperty(theProperty));
			context.put("cached.list.path", path);
			context.put("cached.list.accessed", new Object[] {theParentClass, theProperty});
		}
		
		ReflectionContextState.setLastBeanClassAccessed(context, theParentClass);
		ReflectionContextState.setLastBeanPropertyAccessed(context, theProperty);
	}
}
